<template>
  <div class="live-box">
    <div v-if="wsIsOpen" class="video-container">
      <video class="live-video" id="liveVideo" ref="liveVideo" autoplay controlsList="nodownload noplaybackrate" disablepictureinpicture  @click.prevent="() => {}"></video>
      <div v-show="playType === 'play'" :class="isShowLinear?'linear-box':'controls-box'" ref="controlsBox">
        <div v-if="!isShowLinear" class="controls-left">
          <el-button type="primary" size="small" @click.prevent="captureScreenshot">截图</el-button>
          <el-button type="primary" size="small" @click.prevent="startMark">电子围栏</el-button>
          <div class="linear-controls-box" @click="showLinear">
            <span class="linear-controls-item" v-for="(item,i) in categorizeList" :key="i">
              <span class="item-circle"></span>
              {{ item.type_name }}
              <span class="item-yellow">{{ item.count }}</span>
            </span>
            <el-link><el-icon><ArrowUp /></el-icon>更多</el-link>
          </div>
        </div>
        <div v-if="isShowLinear" class="linear-controls">
          <div class="linear-controls-header">
            <div>标注信息</div>
            <el-link @click="showLinear"><el-icon><ArrowDown /></el-icon>收起</el-link>
          </div>
          <div class="linear-controls-tab">
            <el-radio-group v-model="linearNum" @input="changeLinear">
              <el-radio-button value="1">实时面版</el-radio-button>
              <el-radio-button value="2">数据统计</el-radio-button>
            </el-radio-group>
            <span v-if="linearNum === '2'&&totalMarkList.length < 1">请先设置电子围栏</span>
          </div>
          <div ref="linearContent" class="linear-content">
            <div v-show="linearNum === '1'" ref="linearBox1" :style="linearContentStyle"></div>
            <div v-show="linearNum === '2'" ref="linearBox2" :style="linearContentStyle"></div>
          </div>
        </div>
      </div>
      <canvas id="myCanvas">
        <p>你的浏览器不支持Canvas</p>
      </canvas>
      <div class="mark-box">
        <span class="mark-box-text">查看AI标注</span>
        <el-switch
          v-model="isOPenAiAnnotation"
          active-color="#13ce66"
          inactive-color="#E2E2E3">
        </el-switch>
        <span class="mark-box-text">显示车速</span>
        <el-switch
          v-model="isShowSpeed"
          active-color="#13ce66"
          inactive-color="#E2E2E3">
        </el-switch>
        <span class="mark-box-text">单位km/h</span>
        <el-link v-show="isOPenAiAnnotation" @click="showDialog"><el-icon><Edit /></el-icon>设置标注</el-link>
        <span v-show="isOPenAiAnnotation">标注项：{{ selectAnnotationName }}</span>
      </div>
    </div>
    <div v-else class="no-plugin">
      <img src="@/assets/noPlugin.png" alt="noPlugin">
      <div class="title">检测到插件未启动，可能是以下原因所致。</div>
      <div class="text">
        <span class="text1">1、插件已安装但未启动：请重启插件，启动后刷新网页即可。</span>
        <el-link type="success" @click="openPlugin">启动插件</el-link>
      </div>
      <div class="text">
        <span class="text1">2、未安装插件：请下载并安装插件，安装后重启浏览器。</span>
        <el-link type="success" @click="downLoadPlugin">下载插件</el-link>
      </div>
      <div class="text">
        <span class="text1">3、系统因缺少库文件导致插件报错，无法启动。</span>
        <el-link type="success" @click="downLoadRepairPlugin">下载修复插件</el-link>
      </div>
      <el-link type="success" @click="showPDF">插件安装使用说明</el-link>
    </div>
    <div class="menu-box">
      <el-input v-model="server" placeholder="请输入信令服务器ip或域名" />
      <el-input v-model="port" placeholder="请输入信令服务器端口" />
      <el-input v-model="account" placeholder="请输入账号" />
      <el-input v-model="pwd" placeholder="请输入密码" />
      <el-input v-model="gatewayId" placeholder="请输入网关标识" />
      <el-input v-model="channelNo" placeholder="请输入通道号" />
      <el-button v-if="playType !== 'play'" type="primary" @click="onPlayHandler">观看</el-button>
      <el-button v-else type="primary" @click="close">关闭</el-button>
      <div class="tips">
        <p>账号及密码为飞流可靠安防视频平台时的账号密码。</p>
        <p>网关标识及通道号可在飞流可靠安防视频平台设备管理的网关管理列表及通道管理列表进行查看。</p>
      </div>
    </div>
    <el-dialog
    title="标注选择"
    v-model="dialogVisible"
    width="800px">
      <span>
        <el-checkbox v-model="isSelectAllAnnotation" @change="changeAllAnnotation">全选</el-checkbox>
        <el-checkbox-group v-model="selectAnnotation" @change="handleChecked">
          <el-checkbox v-for="item in allAnnotation" :value="item.label" :key="item.label">{{item.name}}</el-checkbox>
        </el-checkbox-group>
      </span>
    </el-dialog>
  </div>
</template> 
<script setup lang="ts">
  import {ref,onBeforeUnmount,nextTick } from "vue"
  import { ElMessage } from 'element-plus'
  import { Md5 } from "ts-md5";
  import * as echarts from 'echarts';
  import "./style/index.scss"
  import SocketService from '@/utils/socket'
  import {downloadFile,format,updateMarkPoints,categorizeTypes,extractTypeAndCount,countAndSortByType} from '@/utils/index'
  import {SrsRtcPlayerAsync} from '@/utils/srs.sdk'
  import {annotationList} from './annotationList'

  const liveVideo = ref()
  //输入的账号 密码 网关标识 通道号
  const account = ref("")
  const pwd = ref("")
  const gatewayId = ref("")
  const channelNo = ref("")
  const server = ref('')
  const port = ref('')

  //webSocket连接标识 false失败，true成功
  const wsIsOpen = ref(false)
  //webSocket实例
  let ws:WebSocket|null = null
  //webRTC实例
  let sdk:any = null
  //video实例
  let video:any = null
  //playType
  let playType = ref('end')
  //canvas实例
  let canvas:any = null
  let ctx:any = null
  //websocket接收到的消息
  let message:any = null
  // 原始画布宽度
  let originalWidth = 0
  // 原始画布高度
  let originalHeight = 0
  //电子围栏点数
  const markNum = 99999999

  //播放器宽高
  // let video_width:string = '0px'
  // let video_height:string = '0px'
  //是否开启ai标注
  const isOPenAiAnnotation = ref(true)
  const allAnnotation:any = ref([])
  const selectAnnotation:any = ref([])
  const selectAnnotationName = ref('')
  const dialogVisible = ref(false)
  const controlsBox = ref()
  const myChart1:any = ref(null)
  const myChart2:any = ref(null)
  const linearContent = ref()
  const linearBox1 = ref()
  const linearBox2 = ref()
  const isSelectAllAnnotation = ref(true)
  //是否显示速度
  const isShowSpeed = ref(true)

  //是否显示柱状图
  const isShowLinear = ref(false)
  //柱状图数据
  const categorizeList:any = ref([])
  //柱状图切换
  const linearNum = ref('1')
  //电子围栏坐标
  const markPoints:any = ref([])
  //全部进入电子围栏的数据
  const totalMarkList:any = ref([])
  //柱状图样式
  const linearContentStyle = ref({})
  // 是否开启电子围栏模式
  const isMarking = ref(false)
  // websocket连接成功
  const onOpen = () => {
    wsIsOpen.value = true
  }
  // websocket接收到消息
  const onMessage = (e:any) => {
    message = JSON.parse(e.data)
    receiveMsg(message)
  }
  // websocket关闭连接
  const onClose = () => {
    ws = null
    wsIsOpen.value = false
  }
  // 创建websocket连接
  const socket = new SocketService('ws://localhost:1234/',onOpen,onMessage,onClose)
  ws = socket.getWebSocket()

  // 发送消息处理
  const sendMsg = (type:number,data?:any)=>{
    let sendData:any = {}
    switch(type){
      case 1:
        sendData = {
          type:1,
          data:data,
          channel_no:gatewayId.value+channelNo.value
        }
      break;
      case 7:
        sendData = {
          type:7,
          data:{
              "xttp_uuid": account.value,
              "xttp_pwd": Md5.hashStr(pwd.value),
              "xttp_server": server.value,
              "xttp_port": port.value
          }
        }
      break;
      case 8:
      sendData = {
          type:8,
          data:{}
        }
      break;
      case 9:
        sendData = {
          type:9,
          data:{}
        }
      break;
    }
    ws&&ws.send(JSON.stringify(sendData))
  }
  // 接受消息处理
  const receiveMsg = (message:any)=>{
    switch(message.type){
      case 2:
        sdk.setRemoteDescription(message.data)
      break;
      case 3:
        drawAnnotations(message.data)
      break;
      case 10:
        initVideoPlayer()
      break;
    }
  }
  // 初始化视频播放器
  const initVideoPlayer = ()=>{
    srsPlayer()
    initVideoCanvas()
    window.addEventListener('resize', changeVideoCanvasSize)
  }
  //创建SRS连接，交换SDP
  const srsPlayer = ()=>{
    sdk = new SrsRtcPlayerAsync();
    liveVideo.value.srcObject = sdk.stream
    sdk.createOfferAndModifySdp('webrtc://127.0.0.1:1988/live/livestream').then((sdp:any) => {
      sendMsg(1,sdp)
    }).catch((error:any) => {
      console.error('sdkError', error);
    });
  }
  //初始化播放器和canvas
  const initVideoCanvas = ()=>{
    //当视频可以播放，获取video宽高，给canvas画布设置
    video = document.getElementById('liveVideo');
    canvas = document.getElementById('myCanvas')
    ctx = canvas.getContext('2d')
    video.addEventListener('canplay',(_e:any)=>{
      playType.value = 'canplay'
    })
    // 当视频播放时开始绘制标注
    video.addEventListener('play', () =>{
      playType.value = 'play'
      console.log("开始");
      // video_width = video.clientWidth + 'px'
      // video_height = video.clientHeight + 'px'
      changeVideoCanvasSize()
    });
    video.addEventListener('pause', () => { //暂停
      playType.value = 'pause'
      console.log("暂停");
    });
    video.addEventListener('ended', () =>{
      playType.value = 'ended'
      console.log("结束");
      clearAnnotations()
    })
  }
  //将视频宽高赋值给画布
  const changeVideoCanvasSize = ()=>{
    canvas.width = video.clientWidth
    canvas.height = video.clientHeight
  }
  //清除canvas标记
  const clearAnnotations = ()=>{
    // 清除之前的标注
    if(ctx) {
      ctx.clearRect(0, 0, canvas.width,canvas.height);
    }
  }
  //画标注信息
  const drawAnnotations = (message:any,isClear:boolean = true)=>{
    if(isClear){
      clearAnnotations()
      drawMarks()
    }
    if(!isOPenAiAnnotation.value){
      return
    }
    //防止一个人多个标签看 因为第二个页面还没初始化canvas画布
    if(!ctx){
      return
    }
    // 绘制新的标注 绘制一个红色的矩形框
    ctx.strokeStyle = 'red';
    ctx.lineWidth = 1 // 边框宽度
    const w_percentage = canvas.width/message.width // 计算比例
    const h_percentage = canvas.height/message.height // 计算比例
    const filterMessage = message.annotation_item_arr.filter((item:any) => {
      if(selectAnnotation.value.includes(allAnnotation.value[item.id].label)){
        return item
      }
    })
    const annotation_item_arr = filterMessage.map((item:any) => {
        item.xmax = item.xmax_1m/1000000
        item.xmin = item.xmin_1m/1000000
        item.ymax = item.ymax_1m/1000000
        item.ymin = item.ymin_1m/1000000
        item.x_width = (item.xmax - item.xmin) * w_percentage
        item.y_height = (item.ymax - item.ymin) * h_percentage
        item.x = item.xmin * w_percentage
        item.y = item.ymin * h_percentage
        item.type_name = allAnnotation.value[item.id].name
        return item
      }
    )
    const tempList:any[] = []
    annotation_item_arr.forEach((item:any) => {
      // 当有四边形时计算中心点并验证，否则直接通过
      const shouldDraw = markPoints.value.length > 2 
        ? isPointInQuadrilateral(
            item.x + item.x_width / 2,
            item.y + item.y_height / 2
          )
        : true;

      if (shouldDraw) {
        drawAnnotationItem(item);
        tempList.push(item);
        if (markPoints.value.length > 2 ) {
          totalMarkList.value = updateMarkPoints(item,totalMarkList.value)
        }else{
          if(totalMarkList.value.length>0){
            totalMarkList.value = []
          }
        }
      }
    });
    const categorizedData1 = categorizeTypes(tempList);
    const categorizedData2 = extractTypeAndCount(totalMarkList.value);
    categorizeList.value = tempList.length>5?countAndSortByType(tempList).splice(0,5):countAndSortByType(tempList)
    // 处理线性图表显示
    if (isShowLinear.value) {
      if(linearNum.value === '1'){
        initLinear({
          series: categorizedData1.typeCounts,
          yAxis: categorizedData1.typeNames
        });
      }else{
        // console.log(this.totalMarkList);
        totalMarkList.value.length>0&&initLinear({
          series: categorizedData2.typeCounts,
          yAxis: categorizedData2.typeNames
        })
      }
    }
  }
  const initLinear = (data:any)=>{
      const itemHeight = 30; // 每项高度（包含间距）
      const barGap = 10;     // 柱子间距
      const option = {
        animation: false, // 彻底关闭动画
        silent: true, // 关闭交互事件
        xAxis: {
          show:false
        },
        grid: {
          top: '2%',
          bottom:'1%', // 预留滚动条空间
          left: '1%',
          right: '5%',
          containLabel: true
        },
        yAxis: {
          type: 'category',
          data: data.yAxis, // 类别数据
          axisLabel: {
              color: 'white' // 设置 y 轴标签颜色为白色
          },
          axisLine: {
            lineStyle: {
                color: 'white'
              }
          },
          inverse: true
        },
        series: [
          {
            type: 'bar', // 垂直条形图
            data: data.series, // 示例数据
            barWidth: 20, // 设置条形图宽度
            barMaxWidth: 30, // 最大宽度（避免自动调整）
            barGap:barGap + 'px',
            itemStyle: {
              color: 'yellow' // 设置条形图颜色为黄色
            },
            label: {
              show: true,
              position: 'right', // 标签显示在条形图顶部
              color: 'white' // 设置标签文字颜色
            }
          }
        ],
      };
      // 动态计算容器高度
      const linearHeight = linearContent.value.offsetHeight
      const linearContentHeight = data.yAxis.length * itemHeight + data.yAxis.length * barGap
      if(linearHeight > linearContentHeight){
        linearContentStyle.value = { 
          height: `${linearHeight}px` // 总高度 = 项数 * 项高
        };
      }else{
        linearContentStyle.value = { 
          height: `${data.yAxis.length * itemHeight + data.yAxis.length * barGap}px` // 总高度 = 项数 * 项高
        };
      }
      if(linearNum.value === '1'){
        if(myChart1.value === null){
          nextTick(() => {
            myChart1.value = echarts.init(linearBox1.value);
            myChart1.value.setOption(option);
            window.addEventListener('resize', resizeLinear1); // 响应式调整
          });
        }else{
          myChart1.value.setOption(option);
          resizeLinear1()
        }
      }else{
        if(myChart2.value === null){
          nextTick(() => {
            myChart2.value = echarts.init(linearBox2.value);
            myChart2.value.setOption(option);
            window.addEventListener('resize',resizeLinear2); // 响应式调整
          });
        }else{
          myChart2.value.setOption(option);
          resizeLinear2()
        }
      }
  }
  //判断点是否在四边形区域内
  const isPointInQuadrilateral = (x:number, y:number)=> {
    if (markPoints.value.length < 2) {
      return false;
    }
    return isPointInPolygon(x, y, markPoints.value);
  }
  // 判断点是否在多边形内（射线法）
  const isPointInPolygon = (x:number, y:number, polygon:any)=> {
    let inside = false;
    const len = polygon.length;
    for (let i = 0, j = len - 1; i < len; j = i++) {
      const xi = polygon[i].x;
      const yi = polygon[i].y;
      const xj = polygon[j].x;
      const yj = polygon[j].y;
      let intersect = ((yi > y) != (yj > y)) &&
        (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
      if (intersect) inside = !inside;
    }
    return inside;
  }
  // 提取绘制标注的公共方法
  const drawAnnotationItem = (item:any)=>{
    ctx.beginPath();
    ctx.rect(item.x, item.y, item.x_width, item.y_height);
    ctx.stroke();
    ctx.font = '16px Arial';
    ctx.fillStyle = 'red';
    ctx.fillText(item.type_name, item.x + 5, item.y + 20);
    //显示车速，并且是轿车、公交车、卡车
    if(isShowSpeed.value&&(item.id == 2||item.id == 5||item.id == 7)&&parseFloat(item.v) > 0){
      ctx.globalCompositeOperation = 'source-over';
      ctx.fillStyle = '#4747477d'; // 模糊背景颜色
      ctx.fillRect(item.x, item.y - 20, item.x_width, 20); // 调整矩形的大小和位置
      ctx.font = '700 16px Arial';
      ctx.fillStyle = '#FFF15A';
      ctx.fillText(item.v+' km/h', item.x + 5, item.y-5)
    }
  }
  //观看
  const onPlayHandler = ()=>{
    if(!wsIsOpen.value){
      ElMessage.error("插件未启动")
      return
    }
    if(account.value == "" || pwd.value == "" || gatewayId.value == "" || channelNo.value == ""){
      ElMessage.error("请填写所有内容")
      return
    }
    close()
    sendMsg(7)
    sendMsg(9)
  }
  //初始化标签
  const initAnnotation = ()=>{
    allAnnotation.value = annotationList
    allAnnotation.value.forEach((item:any,i:number)=>{
      if(i == 0){
        selectAnnotationName.value = item.name 
      }else if(i<5){
        selectAnnotationName.value += ',' + item.name 
      }
      if(i == 5){
        selectAnnotationName.value += '...'
      }
    })
    selectAnnotation.value = allAnnotation.value.map((item:any)=>{return item.label})
  }
  initAnnotation()
  //显示标注
  const showDialog = ()=>{
    dialogVisible.value = true

  }
  //选择标注方法
  const handleChecked = (value:any)=>{
    selectAnnotation.value = value
    selectAnnotationName.value = ''
    if(value.length < 6){
      value.forEach((item:any,i:number)=>{
        const temp = allAnnotation.value.find((i:any)=>i.label == item)
        if(i == 0){
          selectAnnotationName.value = temp.name
        }else{
          selectAnnotationName.value += ',' + temp.name 
        }
      })
    }else{
      for(let i = 0;i<6;i++){
        if(i == 5){
          selectAnnotationName.value += '...'
        }else{
          const temp = allAnnotation.value.find((j:any)=>j.label == value[i])
          selectAnnotationName.value += temp.name + ','
        }
      }
    }
    (selectAnnotation.value.length == allAnnotation.value.length)?isSelectAllAnnotation.value = true:isSelectAllAnnotation.value = false
  }
  //标签全选和取消全选
  const changeAllAnnotation = ()=>{
    if(isSelectAllAnnotation.value){
      initAnnotation()
    }else{
      selectAnnotation.value = []
      selectAnnotationName.value = ''
    }
  }
  //关闭视频，清空canvas内容
  const close = ()=>{
    sendMsg(8)
    if (sdk != null) {
      sdk.close();
      sdk = null;
    }
    window.removeEventListener('resize',changeVideoCanvasSize)
    clearAnnotations()
    playType.value = 'cancel'
  }
  onBeforeUnmount(()=>{
    close()
  })
  //截图
  const captureScreenshot =()=> {
    // 确保视频正在播放
    if (playType.value === 'play' && video && canvas) {
      clearAnnotations()
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      if(message.type == 3){
        drawAnnotations(message.data,false)
      }
      // 从 canvas 中导出图像
      const dataUrl = canvas.toDataURL('image/png');
      // 创建下载链接并触发下载
      const link = document.createElement('a');
      link.href = dataUrl;
      link.download = `screenshot-${format(new Date(),'yyyyMMddhhmmss')}.png`;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  }
  //标记电子围栏
  const startMark = ()=>{
    // 先移除旧的监听器，避免重复绑定
    liveVideo.value.removeEventListener('click', addMarkPoint);
    markPoints.value = [];
    isMarking.value = true;
    clearAnnotations()
    // 添加新监听器
    liveVideo.value.addEventListener('click', addMarkPoint);
  }
  const addMarkPoint = (e:any) => {
    if (isMarking.value) {
      const rect = liveVideo.value.getBoundingClientRect()
      const x = e.clientX - rect.left
      const y = e.clientY - rect.top

      // 保存原始坐标（相对于视频初始大小）
      const originalX = x * originalWidth / canvas.width
      const originalY = y * originalHeight / canvas.height

      markPoints.value.push({
        x: x,
        y: y,
        originalX: originalX,
        originalY: originalY
      })

      if (markPoints.value.length === markNum) {
        isMarking.value = false
        liveVideo.value.removeEventListener('click', addMarkPoint)
      }
      drawMarks()
    }
  }
  // 绘制标电子围栏
  const drawMarks = () => {
    if (ctx && markPoints.value.length > 0) {
      // 清除画布
      clearAnnotations();
      
      // 绘制标记点
      ctx.beginPath();
      ctx.fillStyle = 'blue';
      markPoints.value.forEach((point:any) => {
        ctx.fillRect(point.x - 2, point.y - 2, 4, 4);
      });
      ctx.stroke();

      // 绘制连接线
      ctx.beginPath();
      ctx.strokeStyle = 'blue';
      ctx.lineWidth = 1;
      // 移动到第一个点
      const firstPoint = markPoints.value[0];
      ctx.moveTo(firstPoint.x, firstPoint.y);
      // 依次连接所有点
      markPoints.value.forEach((point:any) => {
        ctx.lineTo(point.x, point.y);
      });

      // 闭合路径
      ctx.closePath();
      ctx.stroke();
    }
  }
  //电子围栏统计
  const showLinear = ()=>{
    disposeLinear()
    isShowLinear.value = !isShowLinear.value
    if(isShowLinear.value){
      controlsBox.value.style.height = '70%'
    }else{
      controlsBox.value.style.height = '40px'
    }
  }
  const disposeLinear = ()=>{
    if (myChart1.value !== null) {
      myChart1.value.dispose()
      myChart1.value = null
      window.removeEventListener('resize',resizeLinear1)
    }
    if (myChart2.value !== null) {
      myChart2.value.dispose()
      myChart2.value = null
      window.removeEventListener('resize',resizeLinear2)
    }
  }
  const resizeLinear1 = ()=>{
    myChart1.value?.resize()
  }
  const resizeLinear2 = ()=>{
    myChart2.value?.resize()
  }
  const changeLinear = ()=>{
    disposeLinear()
  }
  // 打开插件
  const openPlugin = ()=>{
    window.location.href="xftpplayer://start"
    window.location.reload()
  }
  // 下载插件
  const downLoadPlugin = ()=>{
    downloadFile('https://monitor.zhilianxi.com/d/xplayer_setup_1.1.4.zip')
  }
  // 下载修复插件
  const downLoadRepairPlugin = ()=>{
    downloadFile('https://monitor.zhilianxi.com/d/DirectX_Repair.7z')
  }
  // 插件使用说明
  const showPDF = ()=>{
    window.open(`https://monitor.zhilianxi.com/d/plugin_info.pdf`,'_blank')
  }
</script>
